/**
 * java多线程模式
 * @author andy.rong
 */

package com.myjavalab.thread;

import java.util.Random;

public class WorkThreadPattern {
	public static void main(String[] args) {
		Channel channel = new Channel(5);
		channel.startWorkers();
		new ClientThread1("Alice", channel).start();
		new ClientThread1("Bobby", channel).start();
		new ClientThread1("Chris", channel).start();
		try{
			Thread.sleep(30000);
		}catch(InterruptedException e) {
			
		}
		
		System.exit(0);
	}
}

class ClientThread1 extends Thread{
	private final Channel channel;
	private static Random random = new Random();
	
	public ClientThread1(String name, Channel channel) {
		super(name);
		this.channel = channel;
	}
	
	public void run() {
		try {
			for(int i = 0; true; i++) {
				WorkRequest request = new WorkRequest(getName(), i);
				channel.putRequest(request);
				Thread.sleep(random.nextInt(1000));
			}
		} catch (InterruptedException e) {
			
		}
	}
}

class Channel {
	private static final int MAX_REQUEST = 100;
	private final WorkRequest[] requestQueue;
	private int tail;
	private int head;
	private int count;
	
	private final WorkerThread[] threadPool;
	
	public Channel(int threads) {
		this.requestQueue = new WorkRequest[MAX_REQUEST];
		this.head = 0;
		this.tail = 0;
		this.count = 0;
		threadPool = new WorkerThread[threads];
		for(int i = 0; i < threadPool.length; i++) {
			threadPool[i] = new WorkerThread("Worker - " + i, this);
		}
	}
	
	public void startWorkers() {
		for(int i = 0; i < threadPool.length; i++) {
			threadPool[i].start();
		}
	}
	
	public synchronized void putRequest(final WorkRequest request) {
		while(count >= threadPool.length) {
			try {
				wait();
			} catch (InterruptedException e){
				
			}
		}
		requestQueue[tail] = request;
		tail = (tail + 1) % requestQueue.length;
		count++;
		notifyAll();
		
		/*
		 * Q 8-2
		new Thread(){
			public void run() {
				request.execute();
			}
		}.start();
		 */
	}
	
	public synchronized WorkRequest takeRequest() {
		while(count <= 0) {
			try {
				wait();
			} catch (InterruptedException e){
				
			}
		}
		WorkRequest request = requestQueue[head];
		head = (head + 1) % requestQueue.length;
		count--;
		notifyAll();
		return request;
	}
	
}

class WorkerThread extends Thread{
	private final Channel channel;
	public WorkerThread(String name, Channel channel) {
		super(name);
		this.channel = channel;
	}
	
	public void run() {
		while(true) {
			WorkRequest request = channel.takeRequest();
			request.execute();
		}
	}
}

class WorkRequest {
	private final String name;
	private final int number;
	private final Random random = new Random();
	public WorkRequest(String name, int number) {
		this.name = name;
		this.number = number;
	}
	
	public void execute() {
		System.out.println(Thread.currentThread().getName() + " executes " + this);
		try {
			Thread.sleep(random.nextInt(1000));
		} catch (InterruptedException e) {
			
		}
	}
	
	public String toString() {
		return "[ Request from " + name + " No. " + number + " ]";
	}
}